﻿using System;
using System.Linq;
using System.Collections.Generic;

namespace MilitaryMeteo
{

    static class StringExtensions
    {
        public static IEnumerable<String> SplitInParts(this String s, Int32 partLength)
        {
            if (s == null)
                throw new ArgumentNullException("s");
            if (partLength <= 0)
                throw new ArgumentException("Part length has to be positive.", "partLength");
            for (var i = 0; i < s.Length; i += partLength)
                yield return s.Substring(i, Math.Min(partLength, s.Length - i));
        }
    }
    // Группы метеобилютеня
    public class MeteoGroup
    {
        public int height { get; set; }
        public int dT { get; set; }
        public int AlphaW { get; set; }
        public int V { get; set; }
    }

    public class Result 
    {
        public int Distance { get; set; }
        public int dDsum { get; set; }
        public double ddsum { get; set; }
    }

    public class Calculator
    {

        private class Correction
        {
            private static int[] meteo_heights = { 200, 400, 800, 1200, 1600, 2000, 2400, 3000, 4000, 5000 };
            private MeteoData m_data;

            public Correction(MeteoData m_data)
            {
                this.m_data = m_data;
            }

            public int Distance { get; set; }
            public MeteoGroup MeteoGroup { get; set; }
            public TableValue TableValue { get; set; }
            public WeaponType WeaponType { get; set; }

            private Angle Aw { get { return this.m_data.AlphaOH - this.MeteoGroup.AlphaW; } }

            // Ветер
            private int Wx { get { return -(int)Math.Round(Math.Cos(this.Aw.Radian) * this.MeteoGroup.V); } }
            private int Wz { get { return (int)Math.Round(Math.Sin(this.Aw.Radian) * this.MeteoGroup.V); } }

            // Значения поправок
            private double dDw
            {
                get { return 0.1 * this.TableValue.dXw * this.Wx; }
            }
            private double dDh
            {
                get { return 0.1 * this.TableValue.dXh * this.m_data.dHop; }
            }
            private double dDt
            {
                get { return 0.1 * this.TableValue.dXt * this.MeteoGroup.dT; }
            }
            private double dDtz
            {
                get { return 0.1 * this.TableValue.dXtz * this.m_data.dTz; }
            }
            private double dDv
            {
                get { return this.TableValue.dXv * this.m_data.dVosum; }
            }
            public double dDsum
            {
                get { return this.dDw + this.dDh + this.dDt + this.dDtz + this.dDv; }
            }

            private int ddwz { get { return (int)Math.Round(this.TableValue.dZw * this.Wz * 0.1); } }

            public double ddsum
            { get { return (this.TableValue.Z + ddwz); } }

            private int RoundMeteoNum(int num)
            {
                int min = meteo_heights.Min(x => Math.Abs(x - num));
                return meteo_heights.Last(x => Math.Abs(x - num) == min);
            }

            public int GetMeteoGroupHeight()
            {
                this.TableValue = TableDataParser.GetTableValue(this.WeaponType, this.Distance);
                return this.RoundMeteoNum(this.TableValue.MeteoNum);
            }

            public Result Calculate()
            {
                return new Result()
                {
                    ddsum = (int)Math.Round(this.ddsum),
                    dDsum = (int)Math.Round(this.dDsum),
                    Distance = this.Distance
                };
            }
        }

        private MeteoData m_data;
        private Correction[] corrections;

        public Calculator()
        {
            this.MeteoData.AlphaOH = new Angle();
            corrections = new Correction[3];
            for (int i = 0; i < corrections.Length; i++)
            {
                corrections[i] = new Correction(this.MeteoData);
            }
        }

        public WeaponType WeaponType { get; set; }

        public MeteoData MeteoData
        {
            get
            {
                if (m_data == null)
                    m_data = new MeteoData();
                return m_data;
            }
        }

        public int[] Distance 
        {
            set
            {
                for (int i = 0; i < corrections.Length; i++)
                {
                    corrections[i].Distance = value[i];
                }
            }
        }

        public int[] GetMeteoGroupNumbers()
        {
            int[] numbers = new int[3];
            for (int i = 0; i < corrections.Length; i++)
            {
                corrections[i].WeaponType = this.WeaponType;
                numbers[i] = corrections[i].GetMeteoGroupHeight();
            }
            return numbers;
        }

        public string[] MeteoGroups
        {
            set
            {
                for (int i = 0; i < corrections.Length; i++)
                {
                    this.corrections[i].MeteoGroup = this.ParseMeteoGroup(value[i]);
                }
            }
        }

        private MeteoGroup ParseMeteoGroup(string m_group)
        {
            var data = StringExtensions.SplitInParts(m_group, 2);
            MeteoGroup meteo = new MeteoGroup();
            meteo.dT = int.Parse(data.ElementAt(0));
            meteo.AlphaW = int.Parse(data.ElementAt(1));
            meteo.V = int.Parse(data.ElementAt(2));
            if (meteo.dT >= 50)
            {
                meteo.dT = -meteo.dT + 50;
            }
            return meteo;
        }

        public List<Result> Calculate()
        {
            List<Result> results = new List<Result>();
            for (int i = 0; i < corrections.Length; i++)
            {
                results.Add(corrections[i].Calculate());
            }
            return results;
        }

    }
}